{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fswiss\fcharset0 Tahoma;}{\f1\fswiss\fprq2\fcharset0 Arial;}{\f2\fmodern\fprq1\fcharset0 Courier New;}{\f3\fswiss\fprq2\fcharset0 Tahoma;}{\f4\fnil\fcharset2 Symbol;}}
{\colortbl ;\red0\green0\blue255;\red128\green0\blue0;\red255\green0\blue0;\red0\green128\blue0;}
{\*\generator Msftedit 5.41.21.2509;}\viewkind4\uc1\pard\qc\cf1\f0\fs44 JsonBag\cf0\fs20\par
\pard\par
\pard\qc\fs28 A Single-Class Implementation of JSON\par
in Visual Basic 6.0\fs20\par
\pard\par
\pard\qr Version: 2.0\par
Date: 17 Oct 2013\par
Last Update: 3 Jan 2015\par
\pard\qc\cf1\fs32 Introduction To JSON And JsonBag\cf0\fs20\par
\pard\par
\b\f1\fs24 What Is JSON?\b0\f0\fs20\par
\par
Quoting \cf1\f2 http://json.org/\cf0\f0 :\par
\par
\pard\li720\ri720\cf2\b\f1 JSON\b0  (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the \i JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999\i0 . JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others. These properties make JSON an ideal data-interchange language.\par
\par
JSON is built on two structures:\par
\par
\pard{\pntext\f4\'B7\tab}{\*\pn\pnlvlblt\pnf4\pnindent0{\pntxtb\'B7}}\fi-180\li1620\ri1440 A collection of name/value pairs. In various languages, this is realized as an object, record, struct, dictionary, hash table, keyed list, or associative array.\line\par
{\pntext\f4\'B7\tab}An ordered list of values. In most languages, this is realized as an array, vector, list, or sequence.\par
\pard\li720\ri720\par
These are universal data structures. Virtually all modern programming languages support them in one form or another. It makes sense that a data format that is interchangeable with programming languages also be based on these structures.\cf0\f0\par
\pard\par
\par
\b\f1\fs24 What Is JsonBag?\b0\f0\fs20\par
\par
Using \cf1 JsonBag\cf0  the name/value pair collection (JSON "Object") is a \cf1 JsonBag\cf0  instance where the property \f2 IsArray = False\f0 , and the ordered list of values (JSON "Array") is a \cf1 JsonBag\cf0  instance where \f2 IsArray = True\f0 .\par
\par
In either case a \cf1 JsonBag\cf0  instance is an object that wraps a VB6 \cf1 Collection\cf0  object.  Actually it wraps \i two\i0  instances of \cf1 Collection\cf0  in oder to create a kind of case-sensitive collection because the names used for the attributes (or "Properties") of JSON "Objects" are case sensitive values.\par
\par
Basically a JSON \i document\i0  is implemented as a root anonymous \cf1 JsonBag\cf0  object of either subtype (\f2 IsArray = False\f0  or \f2 IsArray = True\f0 ) that contains zero (\f2 0\f0 ) or more child instances of \cf1 JsonBag\cf0  or simple values.\par
\par
The name \cf1 JsonBag\cf0  is a bit of a play on words, patterned after the name of the intrinsic VB \cf1 PropertyBag\cf0  class.\par
\par
\pard\fi-720\li1440\tx1440\b\f1 Note:\b0\f0\tab\cf1 JsonBag\cf0  is a strict implementation of the standard as described at \cf1\f2 http://json.org/\cf0\f0 .  This means it does not support:\par
\pard\li1440\par
\pard{\pntext\f4\'B7\tab}{\*\pn\pnlvlblt\pnf4\pnindent0{\pntxtb\'B7}}\fi-180\li1620 JavaScript comments,\par
{\pntext\f4\'B7\tab}Un-quoted property names within "Object" nodes, or\par
{\pntext\f4\'B7\tab}Embedded executable JavaScript statements or expressions.\par
\pard\par
\cf1 JsonBag\cf0  does not use the Microsoft JScript scripting engine.\par
\par
\par
\b\f1\fs24 Changes for 2.0\b0\f0\fs20\par
\par
\pard{\pntext\f4\'B7\tab}{\*\pn\pnlvlblt\pnf4\pnindent0{\pntxtb\'B7}}\fi-180\li900 Enumeration (\cf2\f2 For Each\cf0\f0 ) no longer enumerates \cf1 Name\cf0  values, but instead enumerates Items as any normal Collection class would.  Now both JSON "Array" and "Object" type nodes can be enumerated.\par
\pard\par
\pard{\pntext\f4\'B7\tab}{\*\pn\pnlvlblt\pnf4\pnindent0{\pntxtb\'B7}}\fi-180\li900\cf1 Name\cf0  now returns a \cf1 Variant\cf0  instead of a \cf1 String\cf0 .  See \cf1 Name\cf0  below for details.\par
\pard\par
\pard{\pntext\f4\'B7\tab}{\*\pn\pnlvlblt\pnf4\pnindent0{\pntxtb\'B7}}\fi-180\li900\cf1 DecimalMode\cf0  and \cf1 Whitespace\cf0  changes now propagate from the changed node down through its hierarchy of \cf1 JsonBag\cf0  Items (if any).\par
\pard\par
\pard{\pntext\f4\'B7\tab}{\*\pn\pnlvlblt\pnf4\pnindent0{\pntxtb\'B7}}\fi-180\li900 The new \cf1 WhitespaceIndent\cf0   and \cf1 WhitespaceNewLine\cf0  properties modify the result retrieved from the JSON property when \cf1 WhiteSpace\cf0  = \f2 True\f0 .  Previously indenting was always \f2 4\f0  spaces per level and line-breaks were always \f2 vbNewLine\f0 .  Now those are the default values.\par
\pard\par
\pard{\pntext\f4\'B7\tab}{\*\pn\pnlvlblt\pnf4\pnindent0{\pntxtb\'B7}}\fi-180\li900 Fixed long-standing bug in \cf1 Remove\cf0  method.\par
\pard\par
\pard{\pntext\f4\'B7\tab}{\*\pn\pnlvlblt\pnf4\pnindent0{\pntxtb\'B7}}\fi-180\li900 Added compile-time support for VBA7 and WIN64 VBA7 (warning: \cf3 untested\cf0 ).\par
\pard\par
\pard{\pntext\f4\'B7\tab}{\*\pn\pnlvlblt\pnf4\pnindent0{\pntxtb\'B7}}\fi-180\li900 Optimization of JSON parsing (assignment to the\cf1  JSON\cf0  property of a \cf1 JsonBag\cf0 ).\par
\pard\par
\par
\b\f1\fs24 VBA Support\b0\f0\fs20\par
\par
There shouldn't be much effort required to import and modify \cf1 JsonBag\cf0  for use in VBA 6.x as well as VB 6.0.  Excel 2003 (32-bit) seems to work just fine.\par
\par
New with version 2.0 there is untested support for VBA7 and WIN64 VBA7.\par
\par
\par
\pard\qc\cf1\fs32 General Information About JsonBag\cf0\fs20\par
\pard\par
\b\f1\fs24 Exceptions\b0\f0\fs20\par
\par
Due to reliance on exception trapping within \cf1 JsonBag\cf0 , the \i Break on Unhandled Errors\i0  option should be set for testing in the VB6 IDE and in VBA hosts.  This can be found in the General tab of the Tools|Options... dialog under Error Trapping.\par
\par
\par
\b\f1\fs24 JSON vs. JsonBag Data Types\b0\f0\fs20\par
\par
JSON relies on the weak type system in JavaScript which is similar in many ways to how an OLE Variant is used.  Here is a list of the JSON types and the corresponding \cf1 JsonBag\cf0  types:\par
\par
\pard\li1440\ri1440\tx4320\b\f1\fs24 JSON\tab JsonBag\b0\f0\fs20\par
\par
\cf2\f2 String (Unicode)\fs24\'b9\fs20\tab String ("Unicode" i.e. UTF-16LE)\par
\par
Number\tab Double or Decimal\fs24\'b2\fs20\par
\par
Boolean\tab Boolean\par
\par
Null\tab Null\par
\par
Object\tab JsonBag where IsArray = False\par
\par
Array\tab JsonBag where isArray = True\par
\pard\cf0\f0\par
\par
\pard\fi-180\li1620\ri1440\b\'b9\b0  When serialized this is UTF-8.\par
\par
\b\'b2\b0  The actual type used can be selected when a root \cf1 JsonBag\cf0  is first created (and empty).  See the \cf1 DecimalMode\cf0  property below.\par
\pard\par
\par
\b\f1\fs24 Impact Of DecimalMode Setting\b0\f0\fs20\par
\par
\cf1 DecimalMode\cf0  is a read/write Boolean property of the \cf1 JsonBag\cf0  that should be set on the root object of a \cf1 JsonBag\cf0  hierarchy.\par
\par
When \f2 True\f0  the \cf1 Decimal\cf0  type is used for "Number" values.  \cf1 Decimal\cf0  type has the range:\par
\par
\pard\li720 +/-79,228,162,514,264,337,593,543,950,335 with no decimal point;\par
\par
+/-7.9228162514264337593543950335 with 28 places to the right of the decimal;\par
\par
The smallest non-zero number is +/-0.0000000000000000000000000001\par
\par
\pard While this offers greater precision, the actual range of values is less than for \cf1 Double\cf0 .  This means when assigning to the \cf1 JSON\cf0  property (i.e. parsing JSON into a \cf1 JsonBag\cf0  tree) an error 6 (overflow or parse failure) may occur.\par
\par
This is the main reason why \f2 DecimalMode = False\f0  is the default.\par
\par
Another issue is that using \cf1 Decimal\cf0  type values may serialize to more characters of data because it doesn't make use of "scientific" (or "exponential" or "e") notation.  That number format notation will be accepted on input during parsing but not generated on output from serialization.\par
\par
When \f2 False\f0  the \cf1 Double\cf0  type is used for numbers.  \cf1 Double\cf0  has the range:\par
\par
\pard\li720 -1.79769313486231E308 to -4.94065645841247E-324 for negative values;\par
\par
4.94065645841247E-324 to 1.79769313486232E308 for positive values.\par
\pard\par
This gives a broader range but has less precision than \cf1 Decimal\cf0 .  However it is normally safer and thus preferable to use unless your application has a good reason not to for specific cases.\par
\par
\cf1 Double\cf0  values also serialize using "e" notation for very large or very small values, which can result in more compact serialized representation than \cf1 Decimal\cf0  may yield for the same values.\par
\par
This property setting only impacts assignments to the \cf1 JSON\cf0  property, i.e. parsing serialized JSON.  If you directly assign a numeric value the actual type your code used is stored in the object hierarchy data.  Of course as noted it may also impact retrieval from the \cf1 JSON\cf0  property, i.e. serializing the tree into JSON text.\par
\par
It is worth noting that JSON does not preserve this type distinction in its serialization format.  The original type is washed away when you serialize the data and then reparse it into a fresh structure, for example assign a \cf1 JsonBag\cf0 's \cf1 JSON\cf0  back to its \cf1 JSON\cf0  property.\par
\par
\par
\b\f1\fs24 Converting Contents To/From UTF-8 For Transmission\b0\f0\fs20\par
\par
The \cf1 Contents\cf0  property works in OLE/VB "Unicode" i.e. UTF-16LE.  JSON is normally transmitted over networks as UTF-8 encoded characters however.\par
\par
If you need to perform these conversions you have several options:\par
\par
\pard{\pntext\f4\'B7\tab}{\*\pn\pnlvlblt\pnf4\pnindent0{\pntxtb\'B7}}\fi-180\li900 JSON would normally be sent over HTTP/HTTPS and the MSXML XmlHttpRequest object always converts a \cf1 String\cf0  to UTF-8 when used as the body argument on a .\cf1 send\cf0  method call,\line\par
{\pntext\f4\'B7\tab}You can also use an \cf1 ADODB.Stream\cf0  object to perform such conversions explicitly, or\line\par
{\pntext\f4\'B7\tab}You can use the Win32 API calls to perform "wide character to multibyte character" conversions as well.\par
\pard\par
Note that when UTF-8 is used it is common to use naked LFs for whitespace line-breaks.  If you plan to convert serialized JSON to UTF-8 you may want to set the \cf1 WhitespaceNewLine\cf0  property to \f2 vbLf\f0 .\par
\par
\par
\b\f1\fs24 General Information About JsonBag List Items\b0\f0\fs20\par
\par
The child Items ("Properties") of a JSON "Object" \cf1 JsonBag\cf0  are accessed by name (i.e. by Key, a \cf1 String\cf0 ) or by index (a \cf1 Long\cf0 ).  "Object" Item values can only be added or changed by name.\par
\par
Because JSON names are case-sensitive (and this is enforced by JsonBag) the VB/VBA "bang" syntax (\cf2\f2 CollObj![namestring]\cf0\f0 ) should be avoided because the IDE may well adjust the case of \cf2\f2 namestring\cf0\f0  values on you!  Instead try to be careful to be explicit (e.g. \cf2\f2 CollObj.Item("namestring")\cf0\f0  or \cf2\f2 CollObj("namestring")\cf0\f0  instead).\par
\par
Now (starting in version 2.0) Items of both "Objects" and "Arrays" can be enumerated (\cf2\f2 For... Each\cf0\f0 ).\par
\par
\pard\li720\cf2\f2 Dim Item As Variant\par
\par
For Each Item In MyJsonBag.Item("SomeObject")\par
    Debug.Print Item \cf4 'Assumes Item is a simple value.\cf2\par
Next\cf0\f0\par
\pard\par
There is now no way to enumerate Item Name, but this is no longer needed.  JSON "Array" and "Object" \cf1 JsonBag\cf0  Items may also be iterated over by index (\cf1 Long\cf0 ).\par
\par
\pard\li720\cf2\f2 Dim I As Long\par
\par
For I = 1 To MyJsonBag.Item("SomeArray").Count\par
    Debug.Print MyJsonBag.Item("SomeArray").Item(I) \cf4 'Assumes Item is a simple value.\cf2\par
Next\cf0\f0\par
\pard\par
\cf1 JsonBag\cf0  indices range from one (\f2 1\f0 ) to \cf1 Count\cf0 .\par
\par
\par
\pard\qc\cf1\fs32 JsonBag Class Properties\cf0\fs20\par
\pard\par
\b\f1\fs24 Count As Long [read only]\b0\f0\fs20\par
\par
Number of list items in the \cf1 JsonBag\cf0 .\par
\par
Not a recursive count, i.e. does not include counts for the nested items \i within any items that are \cf1 JsonBag\cf0 s themselves\i0 .  Useful for iterating the list or collection by index.\par
\par
\par
\b\f1\fs24 DecimalMode As Boolean [read/write]\b0\f0\fs20\par
\par
Defaults to \f2 False\f0 .\par
\par
The setting of this property determines how numbers are parsed and serialized.  See the discussion \i\f3 Impact Of DecimalMode Setting\i0  above.\par
\par
This should only be set on a \cf1 JsonBag\cf0  instance that is being used as a "root" document node when it is empty.\par
\par
\par
\b\f1\fs24 IsArray As Boolean [read/write until populated]\b0\f0\fs20\par
\par
Once any items have been added to a \cf1 JsonBag\cf0  this becomes read-only.  Clearing the item list (see the \cf1 Clear\cf0  method) makes it read/write once again.\par
\par
The default value is \f2 False\f0 .\par
\par
When \f2 False\f0  the \cf1 JsonBag\cf0  is a JSON "object" that holds a list of named attributes (or properties) accessible by name.\par
\par
When \f2 True\f0  it is a JSON "Array" with an anonymous list of items accessed only by index (from one (1) to \cf1 Count\cf0 ).\par
\par
\par
\b\f1\fs24 Item(ByVal Key As Variant) As Variant [read/write]\b0\f0\fs20\par
\par
Used to create, retrieve, or modify an item.  \i This is the default property of a \cf1 JsonBag\cf0  object.\i0\par
\par
\pard\fi-720\li1440\ri1440\tx1440\b\f1 Note:\tab\b0\f0 When a name (\cf1 String\cf0 ) is used, it is \i case sensitive\i0 .  This means a \cf1 JsonBag\cf0  where \f2 IsArray = False\f0  can have both a named item "A" and a distinct named item "a" in its list.\par
\pard\par
Retrieval works \i either by name or index for JSON "Objects"\i0  but \i only by index for "Arrays."\i0\par
\par
When \f2 IsArray = True\f0 :\par
\par
\pard\li720\ri720 Pass a \f2 Null\f0  as Key to add a new Item ("Element") at the end of the "Array."\par
\par
Pass an Index (\cf1 Long\cf0 ) as Key to assign a new value to an existing Item.  However if the index is greater than .\cf1 Count\cf0  the value is added as a new entry at the end of the "Array."\par
\pard\par
When \f2 IsArray = False\f0 :\par
\par
\pard\li720\ri720 Pass a Name (\cf1 String\cf0 ) as Key.  If the named Item ("Property") exists its value is updated.  If it does not exist a \i new\i0  Item is added.\par
\pard\par
Item reassignment for existing Items (assign new value) is implemented as remove and re-add.  This means changing the value\par
of an "Object's" child Item moves it to the end of the list.  "Array" child Item position can be maintained since the index is passed in to \cf1 Item\cf0 .\par
\par
\par
\b\f1\fs24 JSON As String [read/write]\b0\f0\fs20\par
\par
This property is assigned a JSON string to parse it and create a new JSON document hierarchy.  Assigning to \cf1 JSON\cf0  clears the \cf1 JsonBag\cf0  losing any existing data.\par
\par
You retrieve this property's value to serialize the current JSON hierachy into a JSON string.  The format used varies depending on the setting of the \cf1 Whitespace\cf0  property.\par
\par
Only really useful set or read at the root level.\par
\par
\par
\b\f1\fs24 Name(ByVal Index As Long) As Variant [read-only]\b0\f0\fs20\par
\par
When used to retrieve the name of a JSON "Object's" list Item ("Property") by index a \cf1 Variant String\cf0  is returned.  When used on an "Array"  list Item ("Element") a \cf1 Variant Long\cf0  value is returned.\par
\par
Note that "sparse arrays" are not supported, so when a new Item is added to the end of a \cf1 JsonBag\cf0  "Array" Item by providing an Index > \cf1 Count\cf0  the actual value stored is \cf1 Count\cf0  + 1 and not the supplied Index value.\par
\par
\par
\b\f1\fs24 Version As String() [read-only]\b0\f0\fs20\par
\par
Returns a two-element \cf1 String\cf0  array.  \cf1 Version(0)\cf0  is the major version number and \cf1 Version(1)\cf0  is the minor version number.\par
\par
Seldom needed.\par
\par
\par
\b\f1\fs24 Whitespace As Boolean [read/write]\b0\f0\fs20\par
\par
Defaults to \f2 False\f0 .\par
\par
When \f2 True\f0  the \cf1 JSON\cf0  property serializes to a verbose format with indenting, newlines, and other whitespace added.  This "pretty print" format is most useful for debugging.  Newlines are CR/LF pairs; indenting is done using spaces, not Tab characters..\par
\par
Setting this to \f2 True\f0  is normally only meaningful at the root level, but changes now (as of version 2.0) propagate downward through the hierarchy so you can serialize a child branch easily as well.\par
\par
\par
\b\f1\fs24 WhitespaceIndent As Integer [read/write]\b0\f0\fs20\par
\par
Defaults to \f2 4\f0 .\par
\par
New (with version 2.0).  Number of indenting spaces per nesting level when \cf1 Whitespace\cf0  = \f2 True\f0 .  Must be between 1 and 32.  Changes propagate downward through the hierarchy so you can serialize a child branch easily.\par
\par
\par
\b\f1\fs24 WhitespaceNewLine As String [read/write]\b0\f0\fs20\par
\par
Defaults to \f2 vbNewLine\f0 .\par
\par
New (with version 2.0).  Line-break string used when \cf1 Whitespace\cf0  = \f2 True\f0 .  Must be at least one character.  Changes propagate downward through the hierarchy so you can serialize a child branch easily.\par
\par
\par
\pard\qc\cf1\fs32 JsonBag Class Methods\cf0\fs20\par
\pard\par
\b\f1\fs24 AddNewArray(Optional ByVal Key As Variant = Null) As JsonBag\b0\f0\fs20\par
\par
Creates and adds a new "Array" \cf1 JsonBag\cf0  as a child of the node it is called on.\par
\par
Pass a name as Key when you are calling this method on an "Object" node in the JSON tree.\par
\par
This is really a shortcut for creating a new \cf1 JsonBag\cf0 , setting \f2 IsArray = True\f0  on it, and assigning as a child of an existing \cf1 JsonBag\cf0  node via the \cf1 Item\cf0  property.\par
\par
\par
\b\f1\fs24 AddNewObject(Optional ByVal Key As Variant = Null) As JsonBag\b0\f0\fs20\par
\par
Creates and adds a new "Object" \cf1 JsonBag\cf0  as a child of the node it is called on.\par
\par
Pass a name as Key when you are calling this method on an "Object" node in the JSON tree.\par
\par
This is really a shortcut for creating a new \cf1 JsonBag\cf0 , setting \f2 IsArray = False\f0  on it, and assigning as a child of an existing \cf1 JsonBag\cf0  node via the \cf1 Item\cf0  property.\par
\par
\par
\b\f1\fs24 Clear()\b0\f0\fs20\par
\par
Deletes all of the child Items in a \cf1 JsonBag\cf0  node.  Typically called to clear out a root \cf1 JsonBag\cf0  for reuse.\par
\par
\par
\b\f1\fs24 Exists(ByVal Key As Variant) As Boolean\b0\f0\fs20\par
\par
Only meaningful on "Object" \cf1 JsonBag\cf0 s.  Used to determine whether a named Item exists, by name or index.\par
\par
"Array" type \cf1 JsonBag\cf0  list members indices always range from one (1) to \cf1 Count\cf0 .\par
\par
\par
\b\f1\fs24 NewEnum() As IUnknown [hidden iterator]\b0\f0\fs20\par
\par
Used to enumerate (\cf2\f2 For Each\cf0\f0 ) the child Items of a \cf1 JsonBag\cf0  node.\par
\par
\par
\b\f1\fs24 Remove(ByVal Key As Variant)\b0\f0\fs20\par
\par
Removes a list Item by name or index (only by index for "Array" \cf1 JsonBag\cf0 s).  If the Item does not exist it returns silently without doing anything.\par
\par
}
 